package edu.uci.ics.multicast.host;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import edu.uci.ics.multicast.NodePeer;

public class Rendezvous implements Runnable {

	static final int ADMISSION_PORT = 51111;
	
	private Thread myThread = null;
	private Socket sock = null;

	// list with all nodes on the multicast tree
	private List<NodePeer> listAllNodePeers = null;
	
	public Rendezvous(List<NodePeer> listAllNodePeers, Socket sock) {
		this.listAllNodePeers = listAllNodePeers;
		this.sock= sock; 
		this.myThread = new Thread(this);
	}

	// select a random peer (from listAllNodes) to be the father
	private NodePeer selectNodeToBeFather() {
		return randomSelection();
	}

	// TODO
	private List<NodePeer> proximityAwareSelection() {
		return null;
	}
	
	private int correlationFactor() {
		int proximityFactor = 0;
		return proximityFactor;
	}
	
	// select a random peer (from listAllNodes) to be the father
	private NodePeer randomSelection() {
		int numberOfNodes = listAllNodePeers.size();
		Random generator = new Random();
		int fatherIndex = generator.nextInt(numberOfNodes);
		fatherIndex = (fatherIndex==numberOfNodes) ? fatherIndex-1 : fatherIndex;
		NodePeer fatherNode = listAllNodePeers.get(fatherIndex);
		return fatherNode;
	}
	
	private void replyClientSayingWhoIsHisFather(NodePeer nodeFather) throws IOException {
		String fatherHostName = nodeFather.getHostName();
		ObjectOutputStream sockOutput = new ObjectOutputStream(sock.getOutputStream());
		sockOutput.writeObject(fatherHostName);
		sockOutput.flush();
	}
	
	public synchronized void run() {
		try {
			NodePeer nodePeer = null;
			if (!listAllNodePeers.isEmpty()) {
				NodePeer fatherNode = selectNodeToBeFather();
				replyClientSayingWhoIsHisFather(fatherNode);
				// add new peer in the children list of fatherNode
				nodePeer = new NodePeer(sock.getInetAddress().getHostName(),fatherNode);
				fatherNode.getListNodePeers().add(nodePeer);
				System.out.println("Admission control completed - "
						+ " - Father: " + fatherNode.getHostName()
						+ " - Child: " + nodePeer.getHostName());
			} else {
				nodePeer = new NodePeer(sock.getInetAddress().getHostName(), null);
				System.out.println("Source - Admission control completed");
			}
			listAllNodePeers.add(nodePeer);
			sock.close();
		}
		catch (Exception e){
			e.printStackTrace(System.err);
			return;
		}
	}

	public void start() {
		myThread.start();
	}
	
	public static void main(String argv[]) {
		// list with all nodes on the multicast tree
		List<NodePeer> listAllNodePeers = new LinkedList<NodePeer>();
		try {
			 InetAddress hostName = InetAddress.getByName(argv[0]);
			ServerSocket serverSock = new ServerSocket(ADMISSION_PORT,0,hostName);
			System.out.println("Listening for connnections on "+hostName.getHostAddress()+":"+ADMISSION_PORT);
			while (true) {
				Socket sock = serverSock.accept(); // blocks until someone opens a socket connection
				System.out.println("Client " + sock.getInetAddress().getHostName() + " is connecting to Server");
				Rendezvous rendezvous = new Rendezvous(listAllNodePeers,sock);
				rendezvous.start();
			}
		} catch (IOException e) {
			e.printStackTrace(System.err);
		}
	}

}
